#ifndef __SC_COMPILER_H__
#define __SC_COMPILER_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include <string>
#include <map>
#include <iostream>
#include <ostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>
#include "SCProgram.hpp"
#include "Couple.hpp"
#include "Range.hpp"
#include "Tokeniser.hpp"

#include "SCC_LibConfig.h"

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace SC {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

/**
 *  This class defines a sc file code compiler
 */
class LIBRARY_DECL SCCompiler {

	public:
		
		/// Constructor
		SCCompiler(
			std::ostream & ostream,
			const unsigned int max_comp_errors = 5
		)
		: ostream(ostream), max_comp_errors(max_comp_errors)
		{}
		
		/// Read the given program and store it into the given instance
		void ReadProgram(
			const std::string &,
			SCProgram &,
			const std::string & = "."
		);
		
		/// Write labels to a C macro format providing string, offset and length
		void WriteCMacros(const std::string &) const;
		
	protected:
		
		/// Read the given file
		void readFile(
			const std::string &,
			SCProgram &,
			const std::string & = "."
		);
		
		/// Read an import file command
		void readImport(
			Toolkit::Tokeniser &,
			SCProgram &,
			const std::string & = "."
		);
		
		/// Read a macro
		void readMacro(Toolkit::Tokeniser &);
		
		/// Read a label
		void readLabel(Toolkit::Tokeniser &, SCProgram &);
		
		/// Read the encoding
		void readEncoding(Toolkit::Tokeniser &, SCProgram &, const std::string &);
		
		/// Read a function
		void readFunction(Toolkit::Tokeniser &, SCProgram &);
		
		/// Read a system declaration
		void readSystem(Toolkit::Tokeniser &, SCProgram &);
		
		/// Read the program
		void readProgram(Toolkit::Tokeniser &, SCProgram &);
		
		/// Read an instantiation
		void readInstantiation(Toolkit::Tokeniser &, SCProgram &);
		
		/// Read a scope setting
		void readScope(Toolkit::Tokeniser &, SCProgram &);
		
		/// Read a label value
		const std::string readLabelValue(Toolkit::Tokeniser &, const SCProgram &);
	
		/// Read a range and returns it
		const Types::Range<std::string> getStringRange(Toolkit::Tokeniser &);
		
		/// Read a string array (can be arithmetic expressions) and returns it
		const Types::Range<std::string> getStringArray(Toolkit::Tokeniser &);
		
		/// Read a string expression (can be an arithmetic expression)
		const std::string getStringExpression(Toolkit::Tokeniser &, const std::vector<std::string> & = std::vector<std::string>());
		
		/// Read a schemata
		const std::string readSchema(Toolkit::Tokeniser &, const SCProgram &);
		
		/// Read a system word
		const std::string readSystemWord(Toolkit::Tokeniser &, const SCProgram &);
		
		/// Returns the index of the system given by its name, or -1 if it doesn't exist
		const int getDeclaredSystemIdx(const std::string &, const SCProgram &) const;
		
		/// Returns the index of the system instance given by its name, or -1 if it doesn't exist
		const int getSystemInstanceIdx(const std::string &, const SCProgram &) const;
		
		/// Compress a word according to the code map
		const std::string compress(
			const std::string &,
			const std::vector< Types::Couple<char,std::string> > &,
			const unsigned int
		);
		
		/// Melt to words
		const std::string meltWords(
			const std::string &,
			const std::string &,
			const Toolkit::Tokeniser &
		);
		
		/// Read the character code map file
		const std::vector< Types::Couple<char,std::string> > readCharMap(
			const std::string &,
			const std::string &
		);

		/// Increment the number of compilation errors and throw and exception
		/// if the threshold is passed
		void addError();

		/// Check whether the graph is a directed acyclic graph
		const bool checkDAC(
			std::vector<unsigned int> &,
			const std::vector< std::vector<unsigned int> > &
		) const;
		
		/// Check whether the given token is a ternary string {0,1,WILDCARD}*
		const bool isTernaryWord(const std::string &) const;
		
		/// Check whether the given token is a binary string {0,1}*
		const bool isBinaryWord(const std::string &) const;

		/// Labels
		std::map<std::string,std::string> labels;
		
		/// Macros
		std::map<std::string,int> macros;
		
		/// Output stream
		std::ostream & ostream;
		
		/// Number of compilation errors
		unsigned int nb_comp_errors;

		/// Maximum number of compilation errors before aborting
		unsigned int max_comp_errors;

		/// Sections' state type
		typedef enum State { Empty, Open, Closed };

		/// Encoding and Progran sections' state
		State encoding_section, program_section;

		/// Encoding's sub-sections (done or not)
		bool char_map, func_libs, word_length, func_offset;

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#endif
